package com.xdb.sqlparser.dialect.sql99.parser;

import java.util.ArrayList;
import java.util.List;

import com.xdb.sqlparser.ast.*;
import com.xdb.sqlparser.ast.expr.*;
import com.xdb.sqlparser.ast.statement.*;
import com.xdb.sqlparser.ast.statement.SQLCreateTriggerStatement.TriggerEvent;
import com.xdb.sqlparser.ast.statement.SQLCreateTriggerStatement.TriggerType;
import com.xdb.sqlparser.exception.ParserException;
import com.xdb.sqlparser.parser.*;
/**
 * SQL99规范 语句解析器
 * @author woate
 *
 */
public class SQL99StatementParser extends SQLParser {
	
	protected SQL99ExprParser exprParser;

	protected boolean parseCompleteValues = true;

	protected int parseValuesSize = 3;
	public SQL99StatementParser(String sql) {
		this(sql, null);
	}

	public SQL99StatementParser(String sql, String dbType) {
		this(new SQL99ExprParser(sql, dbType));
	}

	public SQL99StatementParser(SQL99ExprParser exprParser) {
		super(exprParser.getLexer(), exprParser.getDbType());
		this.exprParser = exprParser;
	}

	public SQL99ExprParser getExprParser() {
		return exprParser;
	}

	public List<SQLStatement> parseStatementList() {
		List<SQLStatement> statementList = new ArrayList<SQLStatement>();
		parseStatementList(statementList);
		return statementList;
	}

	public void parseStatementList(List<SQLStatement> statementList) {
		parseStatementList(statementList, -1);
	}

	public void parseStatementList(List<SQLStatement> statementList, int max) {
		for (;;) {
			if (max != -1) {
				if (statementList.size() >= max) {
					return;
				}
			}

			if (lexer.token() == SQLToken.EOF) {
				return;
			}
			if (lexer.token() == SQLToken.END) {
				return;
			}

			if (lexer.token() == (SQLToken.SEMI)) {
				lexer.nextToken();
				continue;
			}

			if (lexer.token() == SQLToken.SELECT) {
				SQLSelectStatement stmt = parseSelect();
				statementList.add(stmt);
				continue;
			}

			if (lexer.token() == (SQLToken.UPDATE)) {
				SQLUpdateStatement stmt = parseUpdateStatement();
				statementList.add(stmt);
				continue;
			}

			if (lexer.token() == (SQLToken.CREATE)) {
				SQLStatement stmt = parseCreate();
				statementList.add(stmt);
				continue;
			}

			if (lexer.token() == (SQLToken.INSERT)) {
				// SQLInsertStatement
				SQLStatement stmt = parseInsert();
				statementList.add(stmt);

				continue;
			}

			if (lexer.token() == (SQLToken.DELETE)) {
				SQLDeleteStatement stmt = parseDeleteStatement();
				statementList.add(stmt);
				continue;
			}

			if (lexer.token() == (SQLToken.EXPLAIN)) {
				SQLExplainStatement stmt = parseExplain();
				statementList.add(stmt);
				continue;
			}

			if (lexer.token() == SQLToken.SET) {
				SQLStatement stmt = parseSet();
				statementList.add(stmt);
				continue;
			}

			if (lexer.token() == SQLToken.ALTER) {
				// SQLAlterTableStatement
				SQLStatement stmt = parseAlter();
				statementList.add(stmt);
				continue;
			}

			if (lexer.token() == SQLToken.DROP) {
				lexer.nextToken();

				if (lexer.token() == SQLToken.TABLE || identifierEquals("TEMPORARY")) {

					SQLDropTableStatement stmt = parseDropTable(false);

					statementList.add(stmt);
					continue;
				} else if (lexer.token() == SQLToken.USER) {
					SQLDropUserStatement stmt = parseDropUser();
					statementList.add(stmt);
					continue;
				} else if (lexer.token() == SQLToken.INDEX) {
					SQLDropIndexStatement stmt = parseDropIndex();
					statementList.add(stmt);
					continue;
				} else if (lexer.token() == SQLToken.VIEW) {
					SQLDropViewStatement stmt = parseDropView(false);
					statementList.add(stmt);
					continue;
				} else if (lexer.token() == SQLToken.TRIGGER) {
					SQLDropTriggerStatement stmt = parseDropTrigger(false);
					statementList.add(stmt);
					continue;
				} else if (lexer.token() == SQLToken.DATABASE) {
					SQLDropDatabaseStatement stmt = parseDropDatabase(false);
					statementList.add(stmt);
					continue;
				} else if (lexer.token() == SQLToken.FUNCTION) {
					SQLDropFunctionStatement stmt = parseDropFunction(false);
					statementList.add(stmt);
					continue;
				} else if (lexer.token() == SQLToken.TABLESPACE) {
					SQLDropTableSpaceStatement stmt = parseDropTablespace(false);
					statementList.add(stmt);
					continue;
				} else if (lexer.token() == SQLToken.PROCEDURE) {
					SQLDropProcedureStatement stmt = parseDropProcedure(false);
					statementList.add(stmt);
					continue;
				} else {
					throw new ParserException("TODO " + lexer.token());
				}
			}

			if (lexer.token() == SQLToken.TRUNCATE) {
				SQLTruncateStatement stmt = parseTruncate();
				statementList.add(stmt);
				continue;
			}

			if (lexer.token() == SQLToken.USE) {
				SQLUseStatement stmt = parseUse();
				statementList.add(stmt);
				continue;
			}

			if (lexer.token() == SQLToken.GRANT) {
				SQLGrantStatement stmt = parseGrant();
				statementList.add(stmt);
				continue;
			}

			if (lexer.token() == SQLToken.REVOKE) {
				SQLRevokeStatement stmt = parseRevoke();
				statementList.add(stmt);
				continue;
			}

			if (lexer.token() == SQLToken.LBRACE || identifierEquals("CALL")) {
				SQLCallStatement stmt = parseCall();
				statementList.add(stmt);
				continue;
			}

			if (identifierEquals("RENAME")) {
				SQLStatement stmt = parseRename();
				statementList.add(stmt);
				continue;
			}

			if (identifierEquals("RELEASE")) {
				SQLReleaseSavePointStatement stmt = parseReleaseSavePoint();
				statementList.add(stmt);
				continue;
			}

			if (identifierEquals("SAVEPOINT")) {
				SQLSavePointStatement stmt = parseSavePoint();
				statementList.add(stmt);
				continue;
			}

			if (identifierEquals("ROLLBACK")) {
				SQLRollbackStatement stmt = parseRollback();

				statementList.add(stmt);
				continue;
			}

			if (identifierEquals("COMMIT")) {
				SQLStatement stmt = parseCommit();

				statementList.add(stmt);
				continue;
			}

			if (lexer.token() == SQLToken.SHOW) {
				SQLStatement stmt = parseShow();

				statementList.add(stmt);
				continue;
			}

			if (lexer.token() == SQLToken.LPAREN) {
				char markChar = lexer.current();
				int markBp = lexer.bp();
				lexer.nextToken();
				if (lexer.token() == SQLToken.SELECT) {
					lexer.reset(markBp, markChar, SQLToken.LPAREN);
					SQLSelectStatement stmt = parseSelect();
					statementList.add(stmt);
					continue;
				}
			}

			if (parseStatementListDialect(statementList)) {
				continue;
			}

			if (lexer.token() == SQLToken.COMMENT) {
				SQLCommentStatement stmt = parseComment();
				statementList.add(stmt);
				continue;
			}
			printError(lexer.token());
		}
	}

	public SQLRollbackStatement parseRollback() {
		lexer.nextToken();

		if (identifierEquals("WORK")) {
			lexer.nextToken();
		}

		SQLRollbackStatement stmt = new SQLRollbackStatement(getDbType());

		if (lexer.token() == SQLToken.TO) {
			lexer.nextToken();

			if (identifierEquals("SAVEPOINT")) {
				lexer.nextToken();
			}

			stmt.setTo(this.exprParser.name());
		}
		return stmt;
	}

	public SQLStatement parseCommit() {
		throw new ParserException("TODO " + lexer.token() + " " + lexer.stringVal());
	}

	public SQLStatement parseShow() {
		throw new ParserException("TODO " + lexer.token() + " " + lexer.stringVal());
	}

	public SQLUseStatement parseUse() {
		accept(SQLToken.USE);
		SQLUseStatement stmt = new SQLUseStatement(getDbType());
		stmt.setDatabase(this.exprParser.name());
		return stmt;
	}

	public SQLGrantStatement parseGrant() {
		accept(SQLToken.GRANT);
		SQLGrantStatement stmt = new SQLGrantStatement(getDbType());

		parsePrivileages(stmt.getPrivileges(), stmt);

		if (lexer.token() == SQLToken.ON) {
			lexer.nextToken();

			if (lexer.token() == SQLToken.PROCEDURE) {
				lexer.nextToken();
				stmt.setObjectType(SQLObjectType.PROCEDURE);
			} else if (lexer.token() == SQLToken.FUNCTION) {
				lexer.nextToken();
				stmt.setObjectType(SQLObjectType.FUNCTION);
			} else if (lexer.token() == SQLToken.TABLE) {
				lexer.nextToken();
				stmt.setObjectType(SQLObjectType.TABLE);
			} else if (lexer.token() == SQLToken.USER) {
				lexer.nextToken();
				stmt.setObjectType(SQLObjectType.USER);
			} else if (lexer.token() == SQLToken.DATABASE) {
				lexer.nextToken();
				stmt.setObjectType(SQLObjectType.DATABASE);
			}

			if (stmt.getObjectType() != null && lexer.token() == SQLToken.COLONCOLON) {
				lexer.nextToken(); // sql server
			}

			SQLExpr expr = this.exprParser.expr();
			if (stmt.getObjectType() == SQLObjectType.TABLE || stmt.getObjectType() == null) {
				stmt.setOn(new SQLExprTableSource(expr));
			} else {
				stmt.setOn(expr);
			}
		}

		if (lexer.token() == SQLToken.TO) {
			lexer.nextToken();
			stmt.setTo(this.exprParser.expr());
		}

		if (lexer.token() == SQLToken.WITH) {
			lexer.nextToken();

			for (;;) {
				if (identifierEquals("MAX_QUERIES_PER_HOUR")) {
					lexer.nextToken();
					stmt.setMaxQueriesPerHour(this.exprParser.primary());
					continue;
				}

				if (identifierEquals("MAX_UPDATES_PER_HOUR")) {
					lexer.nextToken();
					stmt.setMaxUpdatesPerHour(this.exprParser.primary());
					continue;
				}

				if (identifierEquals("MAX_CONNECTIONS_PER_HOUR")) {
					lexer.nextToken();
					stmt.setMaxConnectionsPerHour(this.exprParser.primary());
					continue;
				}

				if (identifierEquals("MAX_USER_CONNECTIONS")) {
					lexer.nextToken();
					stmt.setMaxUserConnections(this.exprParser.primary());
					continue;
				}

				break;
			}
		}

		if (identifierEquals("ADMIN")) {
			lexer.nextToken();
			acceptIdentifier("OPTION");
			stmt.setAdminOption(true);
		}

		if (lexer.token() == SQLToken.IDENTIFIED) {
			lexer.nextToken();
			accept(SQLToken.BY);
			stmt.setIdentifiedBy(this.exprParser.expr());
		}

		return stmt;
	}

	protected void parsePrivileages(List<SQLExpr> privileges, SQLObject parent) {
		for (;;) {
			String privilege = null;
			if (lexer.token() == SQLToken.ALL) {
				lexer.nextToken();
				if (identifierEquals("PRIVILEGES")) {
					privilege = "ALL PRIVILEGES";
				} else {
					privilege = "ALL";
				}
			} else if (lexer.token() == SQLToken.SELECT) {
				privilege = "SELECT";
				lexer.nextToken();
			} else if (lexer.token() == SQLToken.UPDATE) {
				privilege = "UPDATE";
				lexer.nextToken();
			} else if (lexer.token() == SQLToken.DELETE) {
				privilege = "DELETE";
				lexer.nextToken();
			} else if (lexer.token() == SQLToken.INSERT) {
				privilege = "INSERT";
				lexer.nextToken();
			} else if (lexer.token() == SQLToken.INDEX) {
				lexer.nextToken();
				privilege = "INDEX";
			} else if (lexer.token() == SQLToken.TRIGGER) {
				lexer.nextToken();
				privilege = "TRIGGER";
			} else if (lexer.token() == SQLToken.REFERENCES) {
				privilege = "REFERENCES";
				lexer.nextToken();
			} else if (lexer.token() == SQLToken.CREATE) {
				lexer.nextToken();

				if (lexer.token() == SQLToken.TABLE) {
					privilege = "CREATE TABLE";
					lexer.nextToken();
				} else if (lexer.token() == SQLToken.SESSION) {
					privilege = "CREATE SESSION";
					lexer.nextToken();
				} else if (lexer.token() == SQLToken.TABLESPACE) {
					privilege = "CREATE TABLESPACE";
					lexer.nextToken();
				} else if (lexer.token() == SQLToken.USER) {
					privilege = "CREATE USER";
					lexer.nextToken();
				} else if (lexer.token() == SQLToken.VIEW) {
					privilege = "CREATE VIEW";
					lexer.nextToken();
				} else if (lexer.token() == SQLToken.ANY) {
					lexer.nextToken();

					if (lexer.token() == SQLToken.TABLE) {
						lexer.nextToken();
						privilege = "CREATE ANY TABLE";
					} else if (identifierEquals("MATERIALIZED")) {
						lexer.nextToken();
						accept(SQLToken.VIEW);
						privilege = "CREATE ANY MATERIALIZED VIEW";
					} else {
						throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
					}
				} else if (identifierEquals("SYNONYM")) {
					privilege = "CREATE SYNONYM";
					lexer.nextToken();
				} else if (identifierEquals("ROUTINE")) {
					privilege = "CREATE ROUTINE";
					lexer.nextToken();
				} else if (identifierEquals("TEMPORARY")) {
					lexer.nextToken();
					accept(SQLToken.TABLE);
					privilege = "CREATE TEMPORARY TABLE";
				} else {
					throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
				}
			} else if (lexer.token() == SQLToken.ALTER) {
				lexer.nextToken();
				if (lexer.token() == SQLToken.TABLE) {
					privilege = "ALTER TABLE";
					lexer.nextToken();
				} else if (lexer.token() == SQLToken.SESSION) {
					privilege = "ALTER SESSION";
					lexer.nextToken();
				} else if (lexer.token() == SQLToken.ANY) {
					lexer.nextToken();

					if (lexer.token() == SQLToken.TABLE) {
						lexer.nextToken();
						privilege = "ALTER ANY TABLE";
					} else if (identifierEquals("MATERIALIZED")) {
						lexer.nextToken();
						accept(SQLToken.VIEW);
						privilege = "ALTER ANY MATERIALIZED VIEW";
					} else {
						throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
					}
				} else {
					throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
				}
			} else if (lexer.token() == SQLToken.DROP) {
				lexer.nextToken();
				if (lexer.token() == SQLToken.DROP) {
					privilege = "DROP TABLE";
					lexer.nextToken();
				} else if (lexer.token() == SQLToken.SESSION) {
					privilege = "DROP SESSION";
					lexer.nextToken();
				} else if (lexer.token() == SQLToken.ANY) {
					lexer.nextToken();

					if (lexer.token() == SQLToken.TABLE) {
						lexer.nextToken();
						privilege = "DROP ANY TABLE";
					} else if (identifierEquals("MATERIALIZED")) {
						lexer.nextToken();
						accept(SQLToken.VIEW);
						privilege = "DROP ANY MATERIALIZED VIEW";
					} else {
						throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
					}
				} else {
					privilege = "DROP";
				}
			} else if (identifierEquals("USAGE")) {
				privilege = "USAGE";
				lexer.nextToken();
			} else if (identifierEquals("EXECUTE")) {
				privilege = "EXECUTE";
				lexer.nextToken();
			} else if (identifierEquals("PROXY")) {
				privilege = "PROXY";
				lexer.nextToken();
			} else if (identifierEquals("QUERY")) {
				lexer.nextToken();
				acceptIdentifier("REWRITE");
				privilege = "QUERY REWRITE";
			} else if (identifierEquals("GLOBAL")) {
				lexer.nextToken();
				acceptIdentifier("QUERY");
				acceptIdentifier("REWRITE");
				privilege = "GLOBAL QUERY REWRITE";
			} else if (identifierEquals("INHERIT")) {
				lexer.nextToken();
				acceptIdentifier("PRIVILEGES");
				privilege = "INHERIT PRIVILEGES";
			} else if (identifierEquals("EVENT")) {
				lexer.nextToken();
				privilege = "EVENT";
			} else if (identifierEquals("FILE")) {
				lexer.nextToken();
				privilege = "FILE";
			} else if (lexer.token() == SQLToken.GRANT) {
				lexer.nextToken();
				acceptIdentifier("OPTION");
				privilege = "GRANT OPTION";
			} else if (lexer.token() == SQLToken.LOCK) {
				lexer.nextToken();
				acceptIdentifier("TABLES");
				privilege = "LOCK TABLES";
			} else if (identifierEquals("PROCESS")) {
				lexer.nextToken();
				privilege = "PROCESS";
			} else if (identifierEquals("RELOAD")) {
				lexer.nextToken();
				privilege = "RELOAD";
			} else if (identifierEquals("REPLICATION")) {
				lexer.nextToken();
				if (identifierEquals("SLAVE")) {
					lexer.nextToken();
					privilege = "REPLICATION SLAVE";
				} else {
					acceptIdentifier("CLIENT");
					privilege = "REPLICATION CLIENT";
				}
			} else if (lexer.token() == SQLToken.SHOW) {
				lexer.nextToken();

				if (lexer.token() == SQLToken.VIEW) {
					lexer.nextToken();
					privilege = "SHOW VIEW";
				} else {
					acceptIdentifier("DATABASES");
					privilege = "SHOW DATABASES";
				}
			} else if (identifierEquals("SHUTDOWN")) {
				lexer.nextToken();
				privilege = "SHUTDOWN";
			} else if (identifierEquals("SUPER")) {
				lexer.nextToken();
				privilege = "SUPER";

			} else if (identifierEquals("CONTROL")) { // sqlserver
				lexer.nextToken();
				privilege = "CONTROL";
			} else if (identifierEquals("IMPERSONATE")) { // sqlserver
				lexer.nextToken();
				privilege = "IMPERSONATE";
			}

			if (privilege != null) {
				SQLExpr expr = new SQLIdentifierExpr(privilege);

				if (lexer.token() == SQLToken.LPAREN) {
					expr = this.exprParser.primaryRest(expr);
				}

				expr.setParent(parent);
				privileges.add(expr);
			}

			if (lexer.token() == SQLToken.COMMA) {
				lexer.nextToken();
				continue;
			}
			break;
		}
	}

	public SQLRevokeStatement parseRevoke() {
		accept(SQLToken.REVOKE);

		SQLRevokeStatement stmt = new SQLRevokeStatement(getDbType());

		parsePrivileages(stmt.getPrivileges(), stmt);

		if (lexer.token() == SQLToken.ON) {
			lexer.nextToken();

			if (lexer.token() == SQLToken.PROCEDURE) {
				lexer.nextToken();
				stmt.setObjectType(SQLObjectType.PROCEDURE);
			} else if (lexer.token() == SQLToken.FUNCTION) {
				lexer.nextToken();
				stmt.setObjectType(SQLObjectType.FUNCTION);
			} else if (lexer.token() == SQLToken.TABLE) {
				lexer.nextToken();
				stmt.setObjectType(SQLObjectType.TABLE);
			} else if (lexer.token() == SQLToken.USER) {
				lexer.nextToken();
				stmt.setObjectType(SQLObjectType.USER);
			}

			SQLExpr expr = this.exprParser.expr();
			if (stmt.getObjectType() == SQLObjectType.TABLE || stmt.getObjectType() == null) {
				stmt.setOn(new SQLExprTableSource(expr));
			} else {
				stmt.setOn(expr);
			}
		}

		if (lexer.token() == SQLToken.FROM) {
			lexer.nextToken();
			stmt.setFrom(this.exprParser.expr());
		}

		return stmt;
	}

	public SQLSavePointStatement parseSavePoint() {
		acceptIdentifier("SAVEPOINT");
		SQLSavePointStatement stmt = new SQLSavePointStatement(getDbType());
		stmt.setName(this.exprParser.name());
		return stmt;
	}

	public SQLReleaseSavePointStatement parseReleaseSavePoint() {
		acceptIdentifier("RELEASE");
		acceptIdentifier("SAVEPOINT");
		SQLReleaseSavePointStatement stmt = new SQLReleaseSavePointStatement(getDbType());
		stmt.setName(this.exprParser.name());
		return stmt;
	}

	public SQLStatement parseAlter() {
		accept(SQLToken.ALTER);

		if (lexer.token() == SQLToken.TABLE) {
			lexer.nextToken();

			SQLAlterTableStatement stmt = new SQLAlterTableStatement();
			stmt.setName(this.exprParser.name());

			for (;;) {
				if (lexer.token() == SQLToken.DROP) {
					parseAlterDrop(stmt);
				} else if (identifierEquals("ADD")) {
					lexer.nextToken();

					if (lexer.token() == SQLToken.PRIMARY) {
						SQLPrimaryKey primaryKey = this.exprParser.parsePrimaryKey();
						SQLAlterTableAddConstraint item = new SQLAlterTableAddConstraint(primaryKey);
						stmt.getItems().add(item);
					} else if (lexer.token() == SQLToken.IDENTIFIER) {
						SQLAlterTableAddColumn item = parseAlterTableAddColumn();
						stmt.getItems().add(item);
					} else if (lexer.token() == SQLToken.COLUMN) {
						lexer.nextToken();
						SQLAlterTableAddColumn item = parseAlterTableAddColumn();
						stmt.getItems().add(item);
					} else if (lexer.token() == SQLToken.CHECK) {
						SQLCheck check = this.exprParser.parseCheck();
						SQLAlterTableAddConstraint item = new SQLAlterTableAddConstraint(check);
						stmt.getItems().add(item);
					} else if (lexer.token() == SQLToken.CONSTRAINT) {
						SQLConstraint constraint = this.exprParser.parseConstaint();
						SQLAlterTableAddConstraint item = new SQLAlterTableAddConstraint(constraint);
						stmt.getItems().add(item);
					} else if (lexer.token() == SQLToken.FOREIGN) {
						SQLConstraint constraint = this.exprParser.parseForeignKey();
						SQLAlterTableAddConstraint item = new SQLAlterTableAddConstraint(constraint);
						stmt.getItems().add(item);
					} else {
						throw new ParserException("TODO " + lexer.token() + " " + lexer.stringVal());
					}
				} else if (lexer.token() == SQLToken.DISABLE) {
					lexer.nextToken();

					if (lexer.token() == SQLToken.CONSTRAINT) {
						lexer.nextToken();
						SQLAlterTableDisableConstraint item = new SQLAlterTableDisableConstraint();
						item.setConstraintName(this.exprParser.name());
						stmt.getItems().add(item);
					} else {
						acceptIdentifier("KEYS");
						SQLAlterTableDisableKeys item = new SQLAlterTableDisableKeys();
						stmt.getItems().add(item);
					}
				} else if (lexer.token() == SQLToken.ENABLE) {
					lexer.nextToken();
					if (lexer.token() == SQLToken.CONSTRAINT) {
						lexer.nextToken();
						SQLAlterTableEnableConstraint item = new SQLAlterTableEnableConstraint();
						item.setConstraintName(this.exprParser.name());
						stmt.getItems().add(item);
					} else {
						acceptIdentifier("KEYS");
						SQLAlterTableEnableKeys item = new SQLAlterTableEnableKeys();
						stmt.getItems().add(item);
					}
				} else if (lexer.token() == SQLToken.ALTER) {
					lexer.nextToken();
					if (lexer.token() == SQLToken.COLUMN) {
						SQLAlterTableAlterColumn alterColumn = parseAlterColumn();
						stmt.getItems().add(alterColumn);
					} else if (lexer.token() == SQLToken.LITERAL_ALIAS) {
						SQLAlterTableAlterColumn alterColumn = parseAlterColumn();
						stmt.getItems().add(alterColumn);
					} else {
						throw new ParserException("TODO " + lexer.token() + " " + lexer.stringVal());
					}
				} else if (lexer.token() == SQLToken.WITH) {
					lexer.nextToken();
					acceptIdentifier("NOCHECK");
					acceptIdentifier("ADD");
					SQLConstraint check = this.exprParser.parseConstaint();

					SQLAlterTableAddConstraint addCheck = new SQLAlterTableAddConstraint();
					addCheck.setWithNoCheck(true);
					addCheck.setConstraint(check);
					stmt.getItems().add(addCheck);
				} else if (identifierEquals("RENAME")) {
					stmt.getItems().add(parseAlterTableRename());
				} else {
					break;
				}
			}

			return stmt;
		}
		throw new ParserException("TODO " + lexer.token() + " " + lexer.stringVal());
	}

	protected SQLAlterTableItem parseAlterTableRename() {
		acceptIdentifier("RENAME");

		if (lexer.token() == SQLToken.COLUMN) {
			lexer.nextToken();
			SQLAlterTableRenameColumn renameColumn = new SQLAlterTableRenameColumn();
			renameColumn.setColumn(this.exprParser.name());
			accept(SQLToken.TO);
			renameColumn.setTo(this.exprParser.name());
			return renameColumn;
		}

		if (lexer.token() == SQLToken.TO) {
			lexer.nextToken();
			SQLAlterTableRename item = new SQLAlterTableRename();
			item.setTo(this.exprParser.name());
			return item;
		}

		throw new ParserException("TODO " + lexer.token() + " " + lexer.stringVal());
	}

	protected SQLAlterTableAlterColumn parseAlterColumn() {
		lexer.nextToken();
		SQLColumnDefinition column = this.exprParser.parseColumn();

		SQLAlterTableAlterColumn alterColumn = new SQLAlterTableAlterColumn();
		alterColumn.setColumn(column);
		return alterColumn;
	}

	public void parseAlterDrop(SQLAlterTableStatement stmt) {
		lexer.nextToken();

		if (lexer.token() == SQLToken.CONSTRAINT) {
			lexer.nextToken();
			SQLAlterTableDropConstraint item = new SQLAlterTableDropConstraint();
			item.setConstraintName(this.exprParser.name());
			stmt.getItems().add(item);
		} else if (lexer.token() == SQLToken.COLUMN) {
			lexer.nextToken();
			SQLAlterTableDropColumnItem item = new SQLAlterTableDropColumnItem();
			this.exprParser.names(item.getColumns());

			if (lexer.token() == SQLToken.CASCADE) {
				item.setCascade(true);
				lexer.nextToken();
			}

			stmt.getItems().add(item);
		} else if (lexer.token() == SQLToken.LITERAL_ALIAS) {
			SQLAlterTableDropColumnItem item = new SQLAlterTableDropColumnItem();
			this.exprParser.names(item.getColumns());

			if (lexer.token() == SQLToken.CASCADE) {
				item.setCascade(true);
				lexer.nextToken();
			}

			stmt.getItems().add(item);
		} else {
			throw new ParserException("TODO " + lexer.token() + " " + lexer.stringVal());
		}
	}

	public SQLStatement parseRename() {
		throw new ParserException("TODO " + lexer.token() + " " + lexer.stringVal());
	}

	protected SQLDropTableStatement parseDropTable(boolean acceptDrop) {
		if (acceptDrop) {
			accept(SQLToken.DROP);
		}

		SQLDropTableStatement stmt = new SQLDropTableStatement(getDbType());

		if (identifierEquals("TEMPORARY")) {
			lexer.nextToken();
			stmt.setTemporary(true);
		}

		accept(SQLToken.TABLE);

		if (lexer.token() == SQLToken.IF) {
			lexer.nextToken();
			accept(SQLToken.EXISTS);
			stmt.setIfExists(true);
		}

		for (;;) {
			SQLName name = this.exprParser.name();
			stmt.getTableSources().add(new SQLExprTableSource(name));
			if (lexer.token() == SQLToken.COMMA) {
				lexer.nextToken();
				continue;
			}
			break;
		}

		for (;;) {
			if (identifierEquals("RESTRICT")) {
				lexer.nextToken();
				stmt.setRestrict(true);
				continue;
			}

			if (identifierEquals("CASCADE")) {
				lexer.nextToken();
				stmt.setCascade(true);

				if (identifierEquals("CONSTRAINTS")) { // for oracle
					lexer.nextToken();
				}

				continue;
			}

			if (lexer.token() == SQLToken.PURGE) {
				lexer.nextToken();
				stmt.setPurge(true);
				continue;
			}

			break;
		}

		return stmt;
	}

	protected SQLDropSequenceStatement parseDropSequece(boolean acceptDrop) {
		if (acceptDrop) {
			accept(SQLToken.DROP);
		}

		lexer.nextToken();

		SQLName name = this.exprParser.name();

		SQLDropSequenceStatement stmt = new SQLDropSequenceStatement(getDbType());
		stmt.setName(name);
		return stmt;
	}

	protected SQLDropTriggerStatement parseDropTrigger(boolean acceptDrop) {
		if (acceptDrop) {
			accept(SQLToken.DROP);
		}

		lexer.nextToken();

		SQLName name = this.exprParser.name();

		SQLDropTriggerStatement stmt = new SQLDropTriggerStatement(getDbType());
		stmt.setName(name);
		return stmt;
	}

	protected SQLDropViewStatement parseDropView(boolean acceptDrop) {
		if (acceptDrop) {
			accept(SQLToken.DROP);
		}

		SQLDropViewStatement stmt = new SQLDropViewStatement(getDbType());

		accept(SQLToken.VIEW);

		if (lexer.token() == SQLToken.IF) {
			lexer.nextToken();
			accept(SQLToken.EXISTS);
			stmt.setIfExists(true);
		}

		for (;;) {
			SQLName name = this.exprParser.name();
			stmt.getTableSources().add(new SQLExprTableSource(name));
			if (lexer.token() == SQLToken.COMMA) {
				lexer.nextToken();
				continue;
			}
			break;
		}

		if (identifierEquals("RESTRICT")) {
			lexer.nextToken();
			stmt.setRestrict(true);
		} else if (identifierEquals("CASCADE")) {
			lexer.nextToken();

			if (identifierEquals("CONSTRAINTS")) { // for oracle
				lexer.nextToken();
			}

			stmt.setCascade(true);
		}

		return stmt;
	}

	protected SQLDropDatabaseStatement parseDropDatabase(boolean acceptDrop) {
		if (acceptDrop) {
			accept(SQLToken.DROP);
		}

		SQLDropDatabaseStatement stmt = new SQLDropDatabaseStatement(getDbType());

		accept(SQLToken.DATABASE);

		if (lexer.token() == SQLToken.IF) {
			lexer.nextToken();
			accept(SQLToken.EXISTS);
			stmt.setIfExists(true);
		}

		SQLName name = this.exprParser.name();
		stmt.setDatabase(name);

		return stmt;
	}

	protected SQLDropFunctionStatement parseDropFunction(boolean acceptDrop) {
		if (acceptDrop) {
			accept(SQLToken.DROP);
		}

		SQLDropFunctionStatement stmt = new SQLDropFunctionStatement(getDbType());

		accept(SQLToken.FUNCTION);

		if (lexer.token() == SQLToken.IF) {
			lexer.nextToken();
			accept(SQLToken.EXISTS);
			stmt.setIfExists(true);
		}

		SQLName name = this.exprParser.name();
		stmt.setName(name);

		return stmt;
	}

	protected SQLDropTableSpaceStatement parseDropTablespace(boolean acceptDrop) {
		if (acceptDrop) {
			accept(SQLToken.DROP);
		}

		SQLDropTableSpaceStatement stmt = new SQLDropTableSpaceStatement(getDbType());

		accept(SQLToken.TABLESPACE);

		if (lexer.token() == SQLToken.IF) {
			lexer.nextToken();
			accept(SQLToken.EXISTS);
			stmt.setIfExists(true);
		}

		SQLName name = this.exprParser.name();
		stmt.setName(name);

		return stmt;
	}

	protected SQLDropProcedureStatement parseDropProcedure(boolean acceptDrop) {
		if (acceptDrop) {
			accept(SQLToken.DROP);
		}

		SQLDropProcedureStatement stmt = new SQLDropProcedureStatement(getDbType());

		accept(SQLToken.PROCEDURE);

		if (lexer.token() == SQLToken.IF) {
			lexer.nextToken();
			accept(SQLToken.EXISTS);
			stmt.setIfExists(true);
		}

		SQLName name = this.exprParser.name();
		stmt.setName(name);

		return stmt;
	}

	public SQLTruncateStatement parseTruncate() {
		accept(SQLToken.TRUNCATE);
		if (lexer.token() == SQLToken.TABLE) {
			lexer.nextToken();
		}
		SQLTruncateStatement stmt = new SQLTruncateStatement(getDbType());

		if (lexer.token() == SQLToken.ONLY) {
			lexer.nextToken();
			stmt.setOnly(true);
		}

		for (;;) {
			SQLName name = this.exprParser.name();
			stmt.addTableSource(name);

			if (lexer.token() == SQLToken.COMMA) {
				lexer.nextToken();
				continue;
			}

			break;
		}

		for (;;) {
			if (lexer.token() == SQLToken.PURGE) {
				lexer.nextToken();

				if (identifierEquals("SNAPSHOT")) {
					lexer.nextToken();
					acceptIdentifier("LOG");
					stmt.setPurgeSnapshotLog(true);
				} else {
					throw new ParserException("TODO : " + lexer.token() + " " + lexer.stringVal());
				}
				continue;
			}

			if (lexer.token() == SQLToken.RESTART) {
				lexer.nextToken();
				accept(SQLToken.IDENTITY);
				stmt.setRestartIdentity(Boolean.TRUE);
				continue;
			} else if (lexer.token() == SQLToken.SHARE) {
				lexer.nextToken();
				accept(SQLToken.IDENTITY);
				stmt.setRestartIdentity(Boolean.FALSE);
				continue;
			}

			if (lexer.token() == SQLToken.CASCADE) {
				lexer.nextToken();
				stmt.setCascade(Boolean.TRUE);
				continue;
			} else if (lexer.token() == SQLToken.RESTRICT) {
				lexer.nextToken();
				stmt.setCascade(Boolean.FALSE);
				continue;
			}

			break;
		}

		return stmt;
	}

	public SQLStatement parseInsert() {
		SQLInsertStatement insertStatement = new SQLInsertStatement();

		if (lexer.token() == SQLToken.INSERT) {
			accept(SQLToken.INSERT);
		}

		parseInsert0(insertStatement);
		return insertStatement;
	}

	protected void parseInsert0(SQLInsertInto insertStatement) {
		parseInsert0(insertStatement, true);
	}

	protected void parseInsert0_hinits(SQLInsertInto insertStatement) {

	}

	protected void parseInsert0(SQLInsertInto insertStatement, boolean acceptSubQuery) {
		if (lexer.token() == SQLToken.INTO) {
			lexer.nextToken();

			SQLName tableName = this.exprParser.name();
			insertStatement.setTableName(tableName);

			if (lexer.token() == SQLToken.LITERAL_ALIAS) {
				insertStatement.setAlias(as());
			}

			parseInsert0_hinits(insertStatement);

			if (lexer.token() == SQLToken.IDENTIFIER) {
				insertStatement.setAlias(lexer.stringVal());
				lexer.nextToken();
			}
		}

		if (lexer.token() == (SQLToken.LPAREN)) {
			lexer.nextToken();
			this.exprParser.exprList(insertStatement.getColumns(), insertStatement);
			accept(SQLToken.RPAREN);
		}

		if (lexer.token() == SQLToken.VALUES) {
			lexer.nextToken();
			accept(SQLToken.LPAREN);
			SQLInsertStatement.ValuesClause values = new SQLInsertStatement.ValuesClause();
			this.exprParser.exprList(values.getValues(), values);
			insertStatement.setValues(values);
			accept(SQLToken.RPAREN);
		} else if (acceptSubQuery && (lexer.token() == SQLToken.SELECT || lexer.token() == SQLToken.LPAREN)) {
			SQLQueryExpr queryExpr = (SQLQueryExpr) this.exprParser.expr();
			insertStatement.setQuery(queryExpr.getSubQuery());
		}
	}

	public boolean parseStatementListDialect(List<SQLStatement> statementList) {
		return false;
	}

	public SQLDropUserStatement parseDropUser() {
		accept(SQLToken.USER);

		SQLDropUserStatement stmt = new SQLDropUserStatement(getDbType());
		for (;;) {
			SQLExpr expr = this.exprParser.expr();
			stmt.getUsers().add(expr);
			if (lexer.token() == SQLToken.COMMA) {
				lexer.nextToken();
				continue;
			}
			break;
		}

		return stmt;
	}

	public SQLDropIndexStatement parseDropIndex() {
		accept(SQLToken.INDEX);
		SQLDropIndexStatement stmt = new SQLDropIndexStatement(getDbType());
		stmt.setIndexName(this.exprParser.name());

		if (lexer.token() == SQLToken.ON) {
			lexer.nextToken();
			stmt.setTableName(this.exprParser.name());
		}
		return stmt;
	}

	public SQLCallStatement parseCall() {

		boolean brace = false;
		if (lexer.token() == SQLToken.LBRACE) {
			lexer.nextToken();
			brace = true;
		}

		SQLCallStatement stmt = new SQLCallStatement(getDbType());

		if (lexer.token() == SQLToken.QUES) {
			lexer.nextToken();
			accept(SQLToken.EQ);
			stmt.setOutParameter(new SQLVariantRefExpr("?"));
		}

		acceptIdentifier("CALL");

		stmt.setProcedureName(exprParser.name());

		if (lexer.token() == SQLToken.LPAREN) {
			lexer.nextToken();
			exprParser.exprList(stmt.getParameters(), stmt);
			accept(SQLToken.RPAREN);
		}

		if (brace) {
			accept(SQLToken.RBRACE);
			stmt.setBrace(true);
		}

		return stmt;
	}

	public SQLStatement parseSet() {
		accept(SQLToken.SET);
		SQLSetStatement stmt = new SQLSetStatement(getDbType());

		parseAssignItems(stmt.getItems(), stmt);

		return stmt;
	}

	public void parseAssignItems(List<SQLAssignItem> items, SQLObject parent) {
		for (;;) {
			SQLAssignItem item = exprParser.parseAssignItem();
			item.setParent(parent);
			items.add(item);

			if (lexer.token() == SQLToken.COMMA) {
				lexer.nextToken();
				continue;
			} else {
				break;
			}
		}
	}

	public SQLStatement parseCreate() {
		char markChar = lexer.current();
		int markBp = lexer.bp();

		accept(SQLToken.CREATE);

		SQLToken token = lexer.token();

		if (token == SQLToken.TABLE || identifierEquals("GLOBAL")) {
			SQL99CreateTableParser createTableParser = getSQLCreateTableParser();
			return createTableParser.parseCrateTable(false);
		} else if (token == SQLToken.INDEX //
				|| token == SQLToken.UNIQUE //
				|| identifierEquals("NONCLUSTERED") // sql server
		) {
			return parseCreateIndex(false);
		} else if (lexer.token() == SQLToken.SEQUENCE) {
			return parseCreateSequence(false);
		} else if (token == SQLToken.OR) {
			lexer.nextToken();
			accept(SQLToken.REPLACE);
			if (lexer.token() == SQLToken.PROCEDURE) {
				lexer.reset(markBp, markChar, SQLToken.CREATE);
				return parseCreateProcedure();
			}

			if (lexer.token() == SQLToken.VIEW) {
				lexer.reset(markBp, markChar, SQLToken.CREATE);
				return parseCreateView();
			}

			// lexer.reset(mark_bp, mark_ch, Token.CREATE);
			throw new ParserException("TODO " + lexer.token() + " " + lexer.stringVal());
		} else if (token == SQLToken.DATABASE) {
			lexer.nextToken();
			if (identifierEquals("LINK")) {
				lexer.reset(markBp, markChar, SQLToken.CREATE);
				return parseCreateDbLink();
			}

			lexer.reset(markBp, markChar, SQLToken.CREATE);
			return parseCreateDatabase();
		} else if (identifierEquals("PUBLIC") || identifierEquals("SHARE")) {
			lexer.reset(markBp, markChar, SQLToken.CREATE);
			return parseCreateDbLink();
		} else if (token == SQLToken.VIEW) {
			return parseCreateView();
		} else if (token == SQLToken.TRIGGER) {
			return parseCreateTrigger();
		}

		throw new ParserException("TODO " + lexer.token());
	}

	public SQLStatement parseCreateDbLink() {
		throw new ParserException("TODO " + lexer.token());
	}

	public SQLStatement parseCreateTrigger() {
		accept(SQLToken.TRIGGER);

		SQLCreateTriggerStatement stmt = new SQLCreateTriggerStatement(getDbType());
		stmt.setName(this.exprParser.name());

		if (identifierEquals("BEFORE")) {
			stmt.setTriggerType(TriggerType.BEFORE);
			lexer.nextToken();
		} else if (identifierEquals("AFTER")) {
			stmt.setTriggerType(TriggerType.AFTER);
			lexer.nextToken();
		} else if (identifierEquals("INSTEAD")) {
			lexer.nextToken();
			accept(SQLToken.OF);
			stmt.setTriggerType(TriggerType.INSTEAD_OF);
		}

		for (;;) {
			if (lexer.token() == SQLToken.INSERT) {
				lexer.nextToken();
				stmt.getTriggerEvents().add(TriggerEvent.INSERT);
				continue;
			}

			if (lexer.token() == SQLToken.UPDATE) {
				lexer.nextToken();
				stmt.getTriggerEvents().add(TriggerEvent.UPDATE);
				continue;
			}

			if (lexer.token() == SQLToken.DELETE) {
				lexer.nextToken();
				stmt.getTriggerEvents().add(TriggerEvent.DELETE);
				continue;
			}
			break;
		}

		accept(SQLToken.ON);
		stmt.setOn(this.exprParser.name());

		if (lexer.token() == SQLToken.FOR) {
			lexer.nextToken();
			acceptIdentifier("EACH");
			accept(SQLToken.ROW);
			stmt.setForEachRow(true);
		}

		List<SQLStatement> body = this.parseStatementList();
		if (body == null || body.isEmpty()) {
			throw new ParserException("syntax error");
		}
		stmt.setBody(body.get(0));
		return stmt;
	}

	public SQLStatement parseBlock() {
		throw new ParserException("TODO " + lexer.token());
	}

	public SQLStatement parseCreateDatabase() {
		if (lexer.token() == SQLToken.CREATE) {
			lexer.nextToken();
		}

		accept(SQLToken.DATABASE);

		SQLCreateDatabaseStatement stmt = new SQLCreateDatabaseStatement(getDbType());
		stmt.setName(this.exprParser.name());
		return stmt;
	}

	public SQLStatement parseCreateProcedure() {
		throw new ParserException("TODO " + lexer.token());
	}

	public SQLStatement parseCreateSequence(boolean acceptCreate) {
		throw new ParserException("TODO " + lexer.token());
	}

	public SQLStatement parseCreateIndex(boolean acceptCreate) {
		if (acceptCreate) {
			accept(SQLToken.CREATE);
		}

		SQLCreateIndexStatement stmt = new SQLCreateIndexStatement(getDbType());
		if (lexer.token() == SQLToken.UNIQUE) {
			lexer.nextToken();
			if (identifierEquals("CLUSTERED")) {
				lexer.nextToken();
				stmt.setType("UNIQUE CLUSTERED");
			} else {
				stmt.setType("UNIQUE");
			}
		} else if (identifierEquals("FULLTEXT")) {
			stmt.setType("FULLTEXT");
			lexer.nextToken();
		} else if (identifierEquals("NONCLUSTERED")) {
			stmt.setType("NONCLUSTERED");
			lexer.nextToken();
		}

		accept(SQLToken.INDEX);

		stmt.setName(this.exprParser.name());

		accept(SQLToken.ON);

		stmt.setTable(this.exprParser.name());

		accept(SQLToken.LPAREN);

		for (;;) {
			SQLSelectOrderByItem item = this.exprParser.parseSelectOrderByItem();
			item.setParent(stmt);
			stmt.getItems().add(item);
			if (lexer.token() == SQLToken.COMMA) {
				lexer.nextToken();
				continue;
			}
			break;
		}
		accept(SQLToken.RPAREN);

		return stmt;
	}

	public SQL99CreateTableParser getSQLCreateTableParser() {
		return new SQL99CreateTableParser(this.exprParser);
	}

	public SQLSelectStatement parseSelect() {
		SQL99SelectParser selectParser = createSQLSelectParser();
		SQLSelect select = selectParser.select();
		return new SQLSelectStatement(select);
	}

	public SQL99SelectParser createSQLSelectParser() {
		return new SQL99SelectParser(this.exprParser);
	}

	public SQLUpdateStatement parseUpdateStatement() {
		SQLUpdateStatement udpateStatement = createUpdateStatement();

		if (lexer.token() == SQLToken.UPDATE) {
			lexer.nextToken();

			SQLTableSource tableSource = this.exprParser.createSelectParser().parseTableSource();
			udpateStatement.setTableSource(tableSource);
		}

		parseUpdateSet(udpateStatement);

		if (lexer.token() == (SQLToken.WHERE)) {
			lexer.nextToken();
			udpateStatement.setWhere(this.exprParser.expr());
		}

		return udpateStatement;
	}

	protected void parseUpdateSet(SQLUpdateStatement update) {
		accept(SQLToken.SET);

		for (;;) {
			SQLUpdateSetItem item = this.exprParser.parseUpdateSetItem();
			update.addItem(item);

			if (lexer.token() != SQLToken.COMMA) {
				break;
			}

			lexer.nextToken();
		}
	}

	protected SQLUpdateStatement createUpdateStatement() {
		return new SQLUpdateStatement(getDbType());
	}

	public SQLDeleteStatement parseDeleteStatement() {
		SQLDeleteStatement deleteStatement = new SQLDeleteStatement(getDbType());

		if (lexer.token() == SQLToken.DELETE) {
			lexer.nextToken();
			if (lexer.token() == (SQLToken.FROM)) {
				lexer.nextToken();
			}

			if (lexer.token() == SQLToken.COMMENT) {
				lexer.nextToken();
			}

			SQLName tableName = exprParser.name();

			deleteStatement.setTableName(tableName);
		}

		if (lexer.token() == (SQLToken.WHERE)) {
			lexer.nextToken();
			SQLExpr where = this.exprParser.expr();
			deleteStatement.setWhere(where);
		}

		return deleteStatement;
	}

	public SQLCreateTableStatement parseCreateTable() {
		// SQLCreateTableParser parser = new SQLCreateTableParser(this.lexer);
		// return parser.parseCrateTable();
		throw new ParserException("TODO");
	}

	public SQLCreateViewStatement parseCreateView() {
		SQLCreateViewStatement createView = new SQLCreateViewStatement(getDbType());

		if (lexer.token() == SQLToken.CREATE) {
			lexer.nextToken();
		}

		if (lexer.token() == SQLToken.OR) {
			lexer.nextToken();
			accept(SQLToken.REPLACE);
			createView.setOrReplace(true);
		}

		this.accept(SQLToken.VIEW);

		if (lexer.token() == SQLToken.IF || identifierEquals("IF")) {
			lexer.nextToken();
			accept(SQLToken.NOT);
			accept(SQLToken.EXISTS);
			createView.setIfNotExists(true);
		}

		createView.setName(exprParser.name());

		if (lexer.token() == SQLToken.LPAREN) {
			lexer.nextToken();

			for (;;) {
				SQLCreateViewStatement.Column column = new SQLCreateViewStatement.Column();
				SQLExpr expr = this.exprParser.expr();
				column.setExpr(expr);

				if (lexer.token() == SQLToken.COMMENT) {
					lexer.nextToken();
					column.setComment((SQLCharExpr) exprParser.primary());
				}

				column.setParent(createView);
				createView.getColumns().add(column);

				if (lexer.token() == SQLToken.COMMA) {
					lexer.nextToken();
				} else {
					break;
				}
			}

			accept(SQLToken.RPAREN);
		}

		if (lexer.token() == SQLToken.COMMENT) {
			lexer.nextToken();
			SQLCharExpr comment = (SQLCharExpr) exprParser.primary();
			createView.setComment(comment);
		}

		this.accept(SQLToken.AS);

		createView.setSubQuery(new SQL99SelectParser(this.exprParser).select());
		return createView;
	}

	public SQLCommentStatement parseComment() {
		accept(SQLToken.COMMENT);
		SQLCommentStatement stmt = new SQLCommentStatement();

		accept(SQLToken.ON);

		if (lexer.token() == SQLToken.TABLE) {
			stmt.setType(SQLCommentStatement.Type.TABLE);
			lexer.nextToken();
		} else if (lexer.token() == SQLToken.COLUMN) {
			stmt.setType(SQLCommentStatement.Type.COLUMN);
			lexer.nextToken();
		}

		stmt.setOn(this.exprParser.name());

		accept(SQLToken.IS);
		stmt.setComment(this.exprParser.expr());

		return stmt;
	}

	protected SQLAlterTableAddColumn parseAlterTableAddColumn() {
		SQLAlterTableAddColumn item = new SQLAlterTableAddColumn();

		for (;;) {
			SQLColumnDefinition columnDef = this.exprParser.parseColumn();
			item.getColumns().add(columnDef);
			if (lexer.token() == SQLToken.COMMA) {
				lexer.nextToken();
				if (identifierEquals("ADD")) {
					break;
				}
				continue;
			}
			break;
		}
		return item;
	}

	public SQLStatement parseStatement() {
		List<SQLStatement> list = new ArrayList<SQLStatement>();

		this.parseStatementList(list, 1);

		return list.get(0);
	}

	public SQLExplainStatement parseExplain() {
		accept(SQLToken.EXPLAIN);
		if (identifierEquals("PLAN")) {
			lexer.nextToken();
		}

		if (lexer.token() == SQLToken.FOR) {
			lexer.nextToken();
		}

		SQLExplainStatement explain = new SQLExplainStatement(getDbType());

		if (lexer.token() == SQLToken.HINT) {
			explain.setHints(this.exprParser.parseHints());
		}

		explain.setStatement(parseStatement());

		return explain;
	}

	protected SQLAlterTableAddIndex parseAlterTableAddIndex() {
		SQLAlterTableAddIndex item = new SQLAlterTableAddIndex();

		if (lexer.token() == SQLToken.UNIQUE) {
			item.setUnique(true);
			lexer.nextToken();
			if (lexer.token() == SQLToken.INDEX) {
				item.setKeyOrIndex(SQLToken.INDEX.name);
				lexer.nextToken();
			} else if (lexer.token() == SQLToken.KEY) {
				item.setKeyOrIndex(SQLToken.KEY.name);
				lexer.nextToken();
			}
		} else {
			if (lexer.token() == SQLToken.INDEX) {
				item.setKeyOrIndex(SQLToken.INDEX.name);
				accept(SQLToken.INDEX);
			} else if (lexer.token() == SQLToken.KEY) {
				item.setKeyOrIndex(SQLToken.KEY.name);
				accept(SQLToken.KEY);
			}
		}

		if (lexer.token() == SQLToken.LPAREN) {
			lexer.nextToken();
		} else {
			item.setName(this.exprParser.name());
			accept(SQLToken.LPAREN);
		}

		for (;;) {
			SQLSelectOrderByItem column = this.exprParser.parseSelectOrderByItem();
			item.getItems().add(column);
			if (lexer.token() == SQLToken.COMMA) {
				lexer.nextToken();
				continue;
			}
			break;
		}
		accept(SQLToken.RPAREN);
		return item;
	}

	public boolean isParseCompleteValues() {
		return parseCompleteValues;
	}

	public void setParseCompleteValues(boolean parseCompleteValues) {
		this.parseCompleteValues = parseCompleteValues;
	}

	public int getParseValuesSize() {
		return parseValuesSize;
	}

	public void setParseValuesSize(int parseValuesSize) {
		this.parseValuesSize = parseValuesSize;
	}

}
